import {
  mkdirSync,
  readFileSync,
  writeFileSync,
  readdirSync,
  statSync,
  existsSync,
  rmSync,
} from "node:fs";
import { basename, join, sep, parse } from "node:path";
import JsZip from "jszip";
import { dayjs } from "element-plus";

/**
 * zip 递归读取文件夹下的文件流
 * @param zip
 * @param nowPath
 * @param exclude
 * @param alter 只传60分钟内变更的文件（不包含排除的文件）
 */
function readDir(
  zip: any,
  nowPath: string,
  exclude: string[],
  alter: boolean | undefined
) {
  const date = new Date();
  const entryDate = new Date(
    Date.UTC(
      date.getFullYear(),
      date.getMonth(),
      date.getDate(),
      date.getHours(),
      date.getMinutes()
    )
  );

  // 读取目录中的所有文件及文件夹（同步操作）
  let files = readdirSync(nowPath);
  //遍历检测目录中的文件
  files
    .filter((name: string) => !exclude.includes(name))
    .forEach((fileName) => {
      // 当前文件的全路径
      let fillPath = join(nowPath, fileName);

      // 获取一个文件的属性
      let file = statSync(fillPath);
      const diff = Date.now() - file.mtimeMs;
      const minute = diff / 1000 / 60;
      // 如果是目录的话，继续查询
      if (file.isDirectory()) {
        // 压缩对象中生成该目录
        const dir = zip.folder(fileName);
        // （递归）重新检索目录文件
        readDir(dir, fillPath, exclude, alter);
      } else {
        if (alter) {
          // 只读取一小时内变更的文件
          if (minute <= 60) {
            // 读取文件内容
            const content = readFileSync(fillPath);
            // 压缩目录添加文件
            zip.file(fileName, content, {
              date: entryDate, // 使用UTC时间戳
            });
          }
        } else {
          // 读取文件内容
          const content = readFileSync(fillPath);
          // 压缩目录添加文件
          zip.file(fileName, content, {
            date: entryDate, // 使用UTC时间戳
          });
        }
      }
    });
}

/**
 * 开始压缩文件
 * @param output
 * @param web
 * @param alter 只传60分钟内变更的文件（不包含排除的文件）
 */
async function zipFolder(
  { output = __dirname + "/dist.zip", web }: ZipFolderType,
  alter?: boolean
) {
  // 创建 zip 实例
  const zip = new JsZip();

  // zip 递归读取文件夹下的文件流
  readDir(zip, web.source, web.exclude ?? [], alter);

  // 设置压缩格式，开始打包
  return zip
    .generateAsync({
      // nodejs 专用
      type: "nodebuffer",
      // 压缩算法
      compression: "DEFLATE",
      // 压缩级别
      compressionOptions: { level: 9 },
    })
    .then((content: any) => {
      // 将打包的内容写入 当前目录下的 result.zip中
      writeFileSync(output, content, "utf-8");
    });
}

/**
 * 获取文件大小
 * @param target
 */
const readSize = (target: string) => {
  const size = statSync(target).size;
  if (size < 1024) {
    return size + " kb";
  } else {
    return Math.floor(size / 1024) + " mb";
  }
};

/**
 * 开始压缩
 * @param target
 * @param web
 * @param alter 只传60分钟内变更的文件（不包含排除的文件）
 * @param hash 使用当前日期的名称
 */
const onWorker = async ({
  target,
  web,
  alter,
  hash,
}: {
  target: string;
  web: WebType | RunType;
  alter?: boolean;
  hash?: boolean;
}) => {
  // 获取文件名
  const [name] = target.split(sep).reverse();
  // 获取文件名
  const lastName = basename(web?.target || web?.source || "");

  const outputName = web.filename || lastName || name;

  // zip 输出路径和文件名
  const output = `${target}\\${
    hash ? `${web.title}-${dayjs().format("YY年MM月DD日HH时mm分")}` : outputName
  }.zip`;

  // 压缩目标文件夹
  await zipFolder({ output, web }, alter);

  // 获取压缩体积
  const size = readSize(output);

  return {
    name,
    size,
    output,
  };
};

/**
 * 压缩
 * @param target
 * @param web
 * @param alter 只传60分钟内变更的文件（不包含排除的文件）
 * @param hash 使用当前日期的名称
 */
export const JsZipFolder = async ({
  target,
  web,
  alter,
  hash,
}: {
  target: string;
  web: WebType | RunType;
  alter?: boolean;
  hash?: boolean;
}): Promise<JsZipFolderType> => {
  if (existsSync(web.source)) {
    if (!existsSync(target)) {
      mkdirSync(target, { recursive: true });
    }
    return onWorker({ target, web, alter, hash });
  } else {
    return Promise.reject(`压缩失败！${web.source} \n路径不存在`);
  }
};

/**
 * 解压
 * @param target
 */
export const JsZipOutput = async (target: string) => {
  if (existsSync(target)) {
    const { dir, name } = parse(target);
    const zipFilePath = target;
    const extractToPath = `${dir}\\${name}`;

    // 文件夹存在就删除
    if (existsSync(extractToPath)) {
      rmSync(extractToPath, { recursive: true, force: true });
    }

    // 重新创建文件夹
    mkdirSync(extractToPath, { recursive: true });
    // 读取 ZIP 文件内容
    const content = readFileSync(zipFilePath);

    // 创建 JSZip 实例并加载 ZIP 数据
    const zip = new JsZip();
    await zip.loadAsync(content);

    // 遍历 ZIP 文件中的每个条目
    for (const fileName of Object.keys(zip.files)) {
      const file = zip.files[fileName];
      const filePath = join(extractToPath, fileName);

      // 如果条目是文件夹，则创建文件夹
      if (file.dir) {
        mkdirSync(filePath, { recursive: true });
      } else {
        // 否则，将文件内容写入到目标路径
        const fileContent = await file.async("nodebuffer");
        writeFileSync(filePath, fileContent);
      }
    }
  } else {
    return Promise.reject(`解压失败！${target} \n文件不存在`);
  }
};
